#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <iostream>

#include "prot.h"
#include "paging_api.h"
#include "host_helpers.h"

using namespace std;

string flagsToString(u64 flags) {
  string s;
  
  if (flags & PF_PRESENT) 	s += "P ";
  if (flags & PF_WRITE) 	s += "W ";
  if (flags & PF_USER) 		s += "U ";
  if (flags & PF_PWT) 		s += "PWT ";
  if (flags & PF_PCD) 		s += "PCD ";
  if (flags & PF_ACCESSED) 	s += "A ";
  if (flags & PF_DIRTY) 	s += "D ";
  if (flags & PF_MAP_PAGE) 	s += "PS ";
  if (flags & PF_PAT_PTE) 	s += "PAT7 ";
  if (flags & PF_PAT12)		s += "PAT12 ";
  if (flags & PF_UNEXECUTABLE)	s += "XD ";
  
  if (s.size() > 0)
    s.erase(s.size()-1, 1);
  
  return s;
}

const char * cmdToString(int action) {
  switch(action) {
    case CMD_READ:  return "Read";
    case CMD_WRITE: return "Write";
    case CMD_RUN:   return "Run";
    case CMD_INV:   return "Inv";
    case CMD_FLAGS: return "Flags";
    case CMD_MAP:   return "Map";
    case CMD_FLUSH: return "Flush";
    case CMD_END:   return "End";
    case CMD_NOOP:  return "Noop";
  }
  
  return "Unknown action";
}

const char * offsetMaskToString(u64 mask) {
  switch(mask) {
    case OFFSET_MASK_4KB: return "[4kb]";
    case OFFSET_MASK_2MB: return "[2mb]";
    case OFFSET_MASK_1GB: return "[1gb]";
  }
  
  return "[?]";
}

void printPages(u32 *masks, int n) {
  for(int i=0; i<n; ++i) {
    cout << i << ' ' << offsetMaskToString (masks[i]) << endl;
  }
}

void printCommands(test_command *cmd, int n) {
  for(int i=0; i<n; ++i) {
    auto &c = cmd[i];
    cout << cmdToString(c.cmd) << ' ';
    
    if (PAGE_UNDEFINED == c.page)
      cout << "[UNDEF] ";
    else
      cout << '[' << c.page << "] ";
    
    switch(c.cmd) {
      case CMD_FLAGS:
	cout << flagsToString(c.flags);
	if (c.uflags) {
	  cout << " !{" << flagsToString(c.uflags) << "}";
	}
	break;
      case CMD_MAP:
	cout << "-> [" << c.page2 << "]";
	break;
      default:
	cout << c.vaddr;
    }
    
    cout << endl;
  }
}

class Test {
public:
  Test(int fd) {
    int size = fileSize(fd);
    
    data = new char[size];
    read(fd, data, size);
    
    header = (test_header *)data;
    masks = (u32 *)(header + 1);
    commands = (test_command *)(masks + header->n_pages);
  }
  
  ~Test() {
    delete[] data;
  }
  
  void print() {
    cout << header->n_pages << " pages" << endl;
    cout << header->n_commands << " commands" << endl;
    
    cout << "\npages:\n";
    printPages(masks, header->n_pages);
    
    cout << "\ncommands:\n";
    printCommands(commands, header->n_commands);
  }
private:
  char *data;
  test_header *header;
  test_command *commands;
  u32 *masks;
};


int main(int argc, char *argv[]) {
  int fd = paramsIO(argc, argv, "loader [-o outFile] inputFile");
  Test t(fd);
  t.print();
  
  close(fd);
  return 0;
}
